package com.zelix.analy;

import com.arthas.client.ArthasClient;
import com.arthas.client.ArthasTool;
import me.coley.analysis.value.AbstractValue;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.tree.analysis.Frame;

import java.io.IOException;
import java.util.List;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

public class analyFlow {
    public static void main(String[] args) throws IOException {
        JarFile jarFile = null;
        try {
            jarFile = new JarFile("./file/ZKM_bak.jar");
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        ArthasClient client = ArthasTool.getClient();

        if (jarFile == null || client == null) {
            return;
        }

        analy(client, jarFile, "com/zelix/ps", "Y", "([Ljava/lang/Object;)V", 2, "15574592630860");

        client.disconnect();
    }

    public static String analy(ArthasClient client, JarFile jarFile, String cls, String methodName, String methodDes, int index, String value) throws IOException {
        ZipEntry entry = jarFile.getEntry(cls + ".class");

        if (entry != null) {
            ClassReader cr = new ClassReader(jarFile.getInputStream(entry));
            ClassNode cn = new ClassNode();
            cr.accept(cn, ClassReader.EXPAND_FRAMES);

            List<MethodNode> methods = cn.methods;

            for (MethodNode mn : methods) {
                if (methodName.equals(mn.name) && methodDes.equals(mn.desc)) {
                    Frame<AbstractValue>[] frames = Flow.getFrames(cls, mn);
                    if (frames == null) {
                        return null;
                    }
                    InsnList insnList = mn.instructions;
                    boolean flag = false;
                    int varIndex = -1;
                    for (int i = 0; i < insnList.size(); i++) {
                        //未查找到时判断
                        if (!flag && insnList.get(i) instanceof InsnNode && insnList.get(i).getOpcode() == Opcodes.AALOAD) {
                            //数组索引
                            AbstractValue stack = frames[i].getStack(frames[i].getStackSize() - 1);
                            if (stack.getType() == Type.INT_TYPE) {
                                if (stack.getValue() == null) {
                                    return null;
                                }
                                //是否与传入的索引相等
                                if (Integer.valueOf(stack.getValue().toString()) == index) {
                                    flag = true;
                                }
                            }
                        }

                        //查找到以后
                        if (flag) {
                            if (insnList.get(i) instanceof TypeInsnNode) {
                                TypeInsnNode typeInsnNode = (TypeInsnNode) insnList.get(i);
                                if (typeInsnNode.desc.equals("java/lang/Long") && insnList.get(i + 1) instanceof MethodInsnNode) {
                                    MethodInsnNode methodInsnNod = (MethodInsnNode) insnList.get(i + 1);
                                    if (methodInsnNod.owner.equals("java/lang/Long") && methodInsnNod.name.equals("longValue") && methodInsnNod.desc.equals("()J") && insnList.get(i + 2) instanceof VarInsnNode) {
                                        VarInsnNode varInsnNode = (VarInsnNode) insnList.get(i + 2);
                                        varIndex = varInsnNode.var;
                                    }
                                }
                            }
                        }

                        if (varIndex != -1) {
                            if (insnList.get(i) instanceof VarInsnNode && insnList.get(i).getOpcode() == Opcodes.LSTORE
                                    && insnList.get(i - 1) instanceof InsnNode && insnList.get(i - 1).getOpcode() == Opcodes.LXOR
                                    && insnList.get(i - 2) instanceof VarInsnNode && insnList.get(i - 2).getOpcode() == Opcodes.LLOAD
                                    && insnList.get(i - 3) instanceof FieldInsnNode) {
                                FieldInsnNode fieldInsnNode = (FieldInsnNode) insnList.get(i - 3);

                                String rtn = client.sendCommand("ognl '#varLong=@" + cls.replaceAll("/", ".") + "@" + fieldInsnNode.name + ",#varLong^" + value + "L'");
                                return cls + ":" + mn.name + ":" + mn.desc + ":" + TTT.getWatchReturnLong(rtn);
                            }
                        }
                    }
                }
            }
        }
        return null;
    }
}
